package compresionLZP;

import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.Iterator;

import javax.swing.text.TabableView;

import com.colloquial.arithcode2.BitArrayException;
import com.colloquial.arithcode2.BitArrayStream;
import com.colloquial.arithcode2.CompresorAritmetico;
import com.colloquial.arithcode2.ConversionesTiposDatos;
import com.colloquial.arithcode2.DescompresorAritmetico;
import com.colloquial.arithcode2.TablaProbabilidades;

public class CompresorLZP {
	
	TablaLZP tabla;
	
	public byte[] comprimir(byte[] bytesAComprimir) throws Exception{
		tabla = new TablaLZP();
		byte[][] salidaLZP = tabla.comprimir(bytesAComprimir);
		byte[] outPutLong = compresionAritmeticaLongitud(salidaLZP[0]);
		byte[] outPutCar = compresionAritmeticaCaracter(salidaLZP[1]);
		//Obtengo la salida final ya comprimido todo
		byte[] outPut = new byte[outPutCar.length+outPutLong.length+8];
		for (int i = 0; i < outPut.length; i++) {
			if(i<outPutLong.length+4){
				if (i<4){
					outPut[i] = ConversionesTiposDatos.convertirIntABytes(outPutLong.length)[i];
				} else {
					outPut[i] = outPutLong[i-4];
				}
			} else {
				if (i<(8+outPutLong.length)){
					outPut[i] = ConversionesTiposDatos.convertirIntABytes(outPutCar.length)[i-4-outPutLong.length];
				} else {
					outPut[i] = outPutCar[i-8-outPutLong.length];
				}
			}
		}
		return outPut;
		
	}
	
	public byte[] descomprimir(byte[] bytesSDescomprimir) throws Exception{
		int tamanioLong = ConversionesTiposDatos.convertirBytesAInt(Arrays.copyOfRange(bytesSDescomprimir, 0, 4));
		int tamanioCar = ConversionesTiposDatos.convertirBytesAInt(Arrays.copyOfRange(bytesSDescomprimir, tamanioLong+4, tamanioLong+8));
		byte[] longsADesc = new byte[tamanioLong];
		byte[] carsADesc = new byte[tamanioCar];
		for (int i = 0; i < tamanioLong; i++) {
			longsADesc[i] = bytesSDescomprimir[i+4];
		}
		for (int i = 0; i < tamanioCar; i++) {
			carsADesc[i] = bytesSDescomprimir[tamanioLong+i+8];
		}
		byte[] longsDescomprimidas = descompresionAritmeticaLongitud(longsADesc);
		byte[] carsDescomprimidas = descompresionAritmeticaCaracter(carsADesc);
		/*for (int i = 0; i < longsDescomprimidas.length; i++) {
			System.out.print(longsDescomprimidas[i]+" ");
		}*/
		for (int i = 0; i < carsADesc.length; i++) {
			System.out.print(carsADesc[i]+" ");
		} 
		System.out.println();
		System.out.println("Descompresion aritmetico 1º antes del proceso 2º despues del proceso");
		for (int i = 0; i < carsDescomprimidas.length; i++) {
			System.out.print(carsDescomprimidas[i]+" ");
		}
		System.out.println();
		byte[] descompresion = tabla.descomprimir(carsDescomprimidas, longsDescomprimidas);
		return descompresion;
		
	}
	
	// falta calcular probabilidades
	private byte[] compresionAritmeticaCaracter(byte[] caracteres) throws Exception {
		CompresorAritmetico Compresor = new CompresorAritmetico();
		int IdActual = 0;
		int IdContexto = 0;
		Hashtable<Integer, ListaFrecuencias> contextos = new Hashtable<Integer, ListaFrecuencias>();
		for (int i=0; i<caracteres.length; i+=2){
			IdActual = ConversionesTiposDatos.ObtenerIdCaracterUTF16BE(Arrays.copyOfRange(caracteres, i, i+2));
			if (i==0){
				IdContexto = 65536;
				contextos.put(IdContexto, new ListaFrecuencias());
				ListaFrecuencias listaContexto = contextos.get(IdContexto); 
				TablaProbabilidades tablaProbabilidades = new TablaProbabilidades();
				
				Iterator<RegistroPosicionFrecuencia> iteradorListaContexto = listaContexto.iterator();
				RegistroPosicionFrecuencia regAux = null;
				while (iteradorListaContexto.hasNext()){
					regAux = iteradorListaContexto.next();
					tablaProbabilidades.aumentarFrecuencia(regAux.getPosicion(), regAux.getFrecuencia());
				}
				
				Compresor.calcularIntervalo(tablaProbabilidades, IdActual);
				listaContexto.aumentarFrecuencia(IdActual);
			}else{
				IdContexto = ConversionesTiposDatos.ObtenerIdCaracterUTF16BE(Arrays.copyOfRange(caracteres, i-2, i));
				if (!contextos.contains(IdContexto)){
					contextos.put(IdContexto, new ListaFrecuencias());
				}
				
				ListaFrecuencias listaContexto = contextos.get(IdContexto); 
				TablaProbabilidades tablaProbabilidades = new TablaProbabilidades();
				
				Iterator<RegistroPosicionFrecuencia> iteradorListaContexto = listaContexto.iterator();
				RegistroPosicionFrecuencia regAux = null;
				while (iteradorListaContexto.hasNext()){
					regAux = iteradorListaContexto.next();
					tablaProbabilidades.aumentarFrecuencia(regAux.getPosicion(), regAux.getFrecuencia());
				}
				
				Compresor.calcularIntervalo(tablaProbabilidades, IdActual);
				tablaProbabilidades.aumentarFrecuencia(IdActual);
			}
			
		}
		
		IdContexto = ConversionesTiposDatos.ObtenerIdCaracterUTF16BE(Arrays.copyOfRange(caracteres, caracteres.length-2, caracteres.length));
		IdActual = 65537;
		if (!contextos.contains(IdContexto)){
			contextos.put(IdContexto, new ListaFrecuencias()); 
		}	

		ListaFrecuencias listaContexto = contextos.get(IdContexto); 
		TablaProbabilidades tablaProbabilidades = new TablaProbabilidades();
		
		Iterator<RegistroPosicionFrecuencia> iteradorListaContexto = listaContexto.iterator();
		RegistroPosicionFrecuencia regAux = null;
		while (iteradorListaContexto.hasNext()){
			regAux = iteradorListaContexto.next();
			tablaProbabilidades.aumentarFrecuencia(regAux.getPosicion(), regAux.getFrecuencia());
		}	
		
		Compresor.calcularIntervalo(tablaProbabilidades, IdActual);//Esto finaliza la compresion
		
		
		return Compresor.getOutput().GetBytes();
		
	}

	private byte[] compresionAritmeticaLongitud(byte[] longitudes) throws Exception {
		CompresorAritmetico Compresor = new CompresorAritmetico();
		TablaProbabilidades TablaP = new TablaProbabilidades();
		int IdUTF= 0;
		
		for (int i=0; i<longitudes.length ;i+=2){
			IdUTF = ConversionesTiposDatos.ObtenerIdCaracterUTF16BE(Arrays.copyOfRange(longitudes, i, i+2));
			Compresor.calcularIntervalo(TablaP, IdUTF);
			TablaP.aumentarFrecuencia(IdUTF);
		}
		
		Compresor.calcularIntervalo(TablaP, 65537);//Esto finaliza la compresion
		return Compresor.getOutput().GetBytes();
	}

	private byte[] descompresionAritmeticaLongitud(byte[] compL) throws Exception {
		BitArrayStream longitudComp = new BitArrayStream(compL);
		DescompresorAritmetico descomp = new DescompresorAritmetico(longitudComp);
		TablaProbabilidades tablaP= new TablaProbabilidades();
		boolean eof = false;
		while (!eof){
			eof = descomp.descomprimirSiguientePaso(tablaP);
			tablaP.aumentarFrecuencia(descomp.getUltimoDescomprimido());
		}
		byte[] longitudesDescomp = new byte[descomp.getOut().size()];
		longitudesDescomp = descomp.getOut().toByteArray();
		
		return longitudesDescomp;
		
		//System.out.println("Tamanio original   (bytes): "+Datos.length()*2);
		//System.out.println("Tamanio comprimido (bytes): "+Compresor.getOutput().Lenght()/8);
	}

	private byte[] descompresionAritmeticaCaracter(byte[] carsADesc) throws Exception {
		BitArrayStream longitudCar = new BitArrayStream(carsADesc);
		DescompresorAritmetico descomp = new DescompresorAritmetico(longitudCar);
	
		Hashtable<Integer, ListaFrecuencias> contextos = new Hashtable<Integer, ListaFrecuencias>();
		TablaProbabilidades tablaProbabilidades = new TablaProbabilidades();
		
		boolean eof = descomp.descomprimirSiguientePaso(tablaProbabilidades);
		
		ListaFrecuencias listaContexto = new ListaFrecuencias();
		listaContexto.aumentarFrecuencia(descomp.getUltimoDescomprimido());
		contextos.put(65336, listaContexto);
		
		int carAnt;
		while (!eof){
			carAnt = descomp.getUltimoDescomprimido();
			if (!contextos.contains(carAnt))
			{
				contextos.put(carAnt, new ListaFrecuencias());
			}
			listaContexto = contextos.get(carAnt);
			
			Iterator<RegistroPosicionFrecuencia> iteradorListaContexto = listaContexto.iterator();
			RegistroPosicionFrecuencia regAux = null;
			while (iteradorListaContexto.hasNext()){
				regAux = iteradorListaContexto.next();
				tablaProbabilidades.aumentarFrecuencia(regAux.getPosicion(), regAux.getFrecuencia());
			}
			
			eof = descomp.descomprimirSiguientePaso(tablaProbabilidades);
			listaContexto.aumentarFrecuencia(descomp.getUltimoDescomprimido());
		}
		byte[] baDes = new byte[descomp.getOut().size()];
		baDes = descomp.getOut().toByteArray();
		longitudCar.PrintDatos();
		return baDes;	 
	}


	
}
 